# -*- coding: utf-8 -*-
from __future__ import absolute_import

import os
import re
import Queue

from PyQt4.QtGui import QMessageBox
from PyQt4.QtGui import QAbstractItemView
from PyQt4.QtGui import QIcon
from PyQt4.QtGui import QListWidgetItem
from PyQt4.QtGui import QListWidget
from PyQt4.QtGui import QStyle
from PyQt4.QtGui import QLineEdit
from PyQt4.QtGui import QWidget
from PyQt4.QtGui import QHBoxLayout
from PyQt4.QtGui import QPushButton
from PyQt4.QtGui import QCompleter
from PyQt4.QtCore import QObject
from PyQt4.QtCore import QStringList
from PyQt4.QtCore import QThread
from PyQt4.QtCore import Qt
from PyQt4.QtCore import QDir
from PyQt4.QtCore import QFile
from PyQt4.QtCore import QTextStream
from PyQt4.QtCore import SIGNAL

from ninja_ide import resources
from ninja_ide.gui.explorer import explorer_container
from ninja_ide.gui.misc import misc_container
from ninja_ide.gui.main_panel import main_container
from ninja_ide.core import file_manager


class Locator(QObject):

    def __init__(self):
        QObject.__init__(self)
        self._thread = LocateThread()
        self.connect(self._thread, SIGNAL("finished()"), self._load_results)

    def navigate_to(self, function, filePath, isVariable):
        self._thread.find(function, filePath, isVariable)

    def _load_results(self):
        if len(self._thread._results) == 1:
            main_container.MainContainer().open_file(
                fileName=self._thread._results[0][1],
                cursorPosition=self._thread._results[0][2],
                positionIsLineNumber=True)
        elif len(self._thread._results) == 0:
            QMessageBox.information(main_container.MainContainer(),
                self.tr("Definition Not Found"),
                self.tr("This Definition does not belong to this Project."))
        else:
            misc_container.MiscContainer().show_results(self._thread._results)


class LocateThread(QThread):

    def __init__(self):
        QThread.__init__(self)
        self._results = []
        self._cancel = False
        self._locations = []
        self.execute = self.navigate_code

    def find(self, function, filePath, isVariable):
        self.cancel()
        self._filePath = filePath
        self._cancel = False
        if isVariable:
            function_ = r'(\s)*%s(\s)*=\.*' % function
            class_ = r'(\s)*self.%s(\s)*=\.*' % function
        else:
            function_ = r'(\s)*def(\s)+%s(\s)*\(.*' % function
            class_ = r'(\s)*class(\s)+%s(\s)*\(.*' % function
        self.patFunction = re.compile(function_)
        self.patClass = re.compile(class_)
        self.start()

    def find_code_location(self):
        self.cancel()
        self._cancel = False
        function_ = r'(\s)*def(\s)+(\w)+(\s)*\(.*'
        class_ = r'(\s)*class(\s)+(\w)+.*'
        self.patFunction = re.compile(function_)
        self.patClass = re.compile(class_)
        self.start()

    def run(self):
        self.execute()

    def navigate_code(self):
        explorerContainer = explorer_container.ExplorerContainer()
        projects = explorerContainer.get_opened_projects()
        project = None
        for p in projects:
            if self._filePath.startswith(p):
                project = p
                break
        #Search in files
        if not project:
            fileName = file_manager.get_basename(self._filePath)
            self._grep_file(self._filePath, fileName)
            return
        queue = Queue.Queue()
        queue.put(project)
        file_filter = QDir.Files | QDir.NoDotAndDotDot | QDir.Readable
        dir_filter = QDir.Dirs | QDir.NoDotAndDotDot | QDir.Readable
        while not self._cancel and not queue.empty():
            current_dir = QDir(queue.get())
            #Skip not readable dirs!
            if not current_dir.isReadable():
                continue

            #Collect all sub dirs!
            current_sub_dirs = current_dir.entryInfoList(dir_filter)
            for one_dir in current_sub_dirs:
                queue.put(one_dir.absoluteFilePath())

            current_sub_dirs = current_dir.entryInfoList(dir_filter)
            #all files in sub_dir first apply the filters
            current_files = current_dir.entryInfoList(
                ['*.py'], file_filter)
            #process all files in current dir!
            for one_file in current_files:
                if one_file.fileName() != '__init__.py':
                    self._grep_file(one_file.absoluteFilePath(),
                        one_file.fileName())

    def locate_code(self):
        projects = explorer_container.ExplorerContainer().get_opened_projects()
        queue = Queue.Queue()
        for project in projects:
            queue.put(project)
        file_filter = QDir.Files | QDir.NoDotAndDotDot | QDir.Readable
        dir_filter = QDir.Dirs | QDir.NoDotAndDotDot | QDir.Readable
        while not self._cancel and not queue.empty():
            current_dir = QDir(queue.get())
            #Skip not readable dirs!
            if not current_dir.isReadable():
                continue

            #Collect all sub dirs!
            current_sub_dirs = current_dir.entryInfoList(dir_filter)
            for one_dir in current_sub_dirs:
                queue.put(one_dir.absoluteFilePath())

            current_sub_dirs = current_dir.entryInfoList(dir_filter)
            #all files in sub_dir first apply the filters
            current_files = current_dir.entryInfoList(
                ['*.py'], file_filter)
            #process all files in current dir!
            for one_file in current_files:
                self._grep_file_locate(one_file.absoluteFilePath(),
                    one_file.fileName())

    def _grep_file(self, file_path, file_name):
        file_object = QFile(file_path)
        if not file_object.open(QFile.ReadOnly):
            return

        stream = QTextStream(file_object)
        lines = []
        line_index = 0
        line = stream.readLine()
        while not self._cancel:
            if self.patFunction.match(line) or self.patClass.match(line):
                #fileName - path - lineNumber - lineContent
                lines.append((unicode(file_name), unicode(file_path),
                    line_index, unicode(line)))
            #take the next line!
            line = stream.readLine()
            if line.isNull():
                break
            line_index += 1
        if lines:
            self._results += lines

    def _grep_file_locate(self, file_path, file_name):
        file_object = QFile(file_path)
        if not file_object.open(QFile.ReadOnly):
            return
        #type - file_name - file_path
        self._locations.append(('f', unicode(file_name), unicode(file_path)))

        stream = QTextStream(file_object)
        lines = []
        line_index = 0
        line = stream.readLine()
        while not self._cancel:
            if self.patFunction.match(line):
                line = unicode(line)
                func_name = line[:line.find('(')].split('def')[1].strip()
                #type - function name - file_name - file_path - lineNumber
                lines.append(('m', func_name, unicode(file_name),
                    unicode(file_path), line_index))
            elif self.patClass.match(line):
                line = unicode(line)
                if line.find('(') > 0:
                    class_name = line[:line.find('(')].split('class')[1].strip()
                else:
                    class_name = line[:line.find(':')].split('class')[1].strip()
                #type - class name - file_name - file_path - lineNumber
                lines.append(('c', class_name, unicode(file_name),
                    unicode(file_path), line_index))
            #take the next line!
            line = stream.readLine()
            if line.isNull():
                break
            line_index += 1
        if lines:
            self._locations += lines

    def cancel(self):
        self._cancel = True
        self._results = []
        self._locations = []


class CodeLocatorWidget(QWidget):

    def __init__(self, parent):
        QWidget.__init__(self, parent)
        #Parent is StatusBar
        self._parent = parent
        self._thread = LocateThread()
        self._thread.execute = self._thread.locate_code

        hLocator = QHBoxLayout(self)
        hLocator.setContentsMargins(0, 0, 0, 0)
        self._btnClose = QPushButton(
            self.style().standardIcon(QStyle.SP_DialogCloseButton), '')
        self._btnGo = QPushButton(
            self.style().standardIcon(QStyle.SP_ArrowRight), 'Go!')
        self._lineLocate = QLineEdit()
        self._completer = LocateCompleter(self)
        self._lineLocate.setCompleter(self._completer)

        hLocator.addWidget(self._btnClose)
        hLocator.addWidget(self._lineLocate)
        hLocator.addWidget(self._btnGo)

        self.connect(self._completer, SIGNAL("activated(QString)"),
            self._locate_code)
        self.connect(self._lineLocate, SIGNAL("textChanged(QString)"),
            self._set_prefix)

    def explore_code(self):
        self._thread.find_code_location()

    def _set_prefix(self, prefix):
        self._completer.setCompletionPrefix(prefix)

    def _locate_code(self, option):
        print option
        self._parent.hide()

    def show_suggestions(self):
#        cr = self._lineLocate.cursorRect()
#        self._completer.complete(cr)
        self.listWidget = QListWidget()
        self.listWidget.addItems(['diego', 'gato'])
        self.listWidget.show()


class LocateCompleter(QCompleter):

    def __init__(self, parent):
        QCompleter.__init__(self, parent)
        self._parent = parent

        self.icons = {'m': QIcon(resources.IMAGES['function']),
            'f': QIcon(resources.IMAGES['tree-python']),
            'c': QIcon(resources.IMAGES['class'])}

        self.setCompletionMode(QCompleter.PopupCompletion)
        self.setCaseSensitivity(Qt.CaseInsensitive)
        self.setModelSorting(QCompleter.CaseSensitivelySortedModel)
        self._popupView = QListWidget()
        self.popup().setSelectionMode(QAbstractItemView.SingleSelection)

    def complete(self, cr):
        model = self.obtain_model_items(self._parent._thread._locations)
        self.setModel(model)
        cr.setWidth(self._parent._lineLocate.width())
        QCompleter.complete(self, cr)

    def obtain_model_items(self, locations):
#        listS = [l[1] for l in locations]
#        stringList = QStringList(listS)
#        q = stringList.filter("etti")
#        for w in q:
#            print w
        self._popupView.clear()
        for loc in locations:
            self._popupView.addItem(QListWidgetItem(self.icons[loc[0]], loc[1]))
        return self._popupView.model()
